package com.chenfan.mcn.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.chenfan.mcn.config.UserVoContextHolder;
import com.chenfan.mcn.constant.NumberCst;
import com.chenfan.mcn.dao.IncomeContractMapper;
import com.chenfan.mcn.enums.*;
import com.chenfan.mcn.event.EventPublisher;
import com.chenfan.mcn.event.OperateLogEvent;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.common.extension.spring.TransactionUtils;
import com.chenfan.mcn.model.IncomeContract;
import com.chenfan.mcn.model.dto.ApprovalFlowDTO;
import com.chenfan.mcn.model.dto.ChangeApprovalDTO;
import com.chenfan.mcn.model.dto.LiveContractFinanceSettlementDTO;
import com.chenfan.mcn.mq.producer.LiveContactOscMessageProducer;
import com.chenfan.mcn.service.ApprovalCallbackService;
import com.chenfan.mcn.service.ApprovalFlowService;
import com.chenfan.mcn.service.LiveContactOfflineCommissionService;
import com.chenfan.mcn.utils.BusinessAssert;
import com.chenfan.mcn.utils.CommonUtil;
import com.chenfan.mcn.utils.ValidatorUtil;
import com.chenfan.process.vo.ApprovalOperationVO;
import com.chenfan.process.vo.NextNotifyVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;

/**
 * 直播合同-线下佣金-服务接口实现
 * @author wulg
 * @date 2023/6/1
 */
@SuppressWarnings({"deprecation", "AlibabaUndefineMagicConstant"})
@Slf4j
@Service
public class LiveContactOfflineCommissionServiceImpl implements LiveContactOfflineCommissionService, ApprovalCallbackService {

    @Autowired
    private IncomeContractMapper incomeContractMapper;

    @Autowired
    private ApprovalFlowService approvalFlowService;

    @Autowired
    private LiveContactOscMessageProducer liveContactOscMessageProducer;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long changeStatus(Long incomeContractId, Integer status) {
        IncomeContract incomeContract = this.getIncomeContract(incomeContractId);
        if(NumberCst.ONE.equals(status)) {
            //提起审批
            BusinessAssert.isTrue(OfflineCommissionSettleStatusEnum.isUnsettled(incomeContract.getOfflineCommissionSettleStatus()), McnErrorCode.BUSINESS_ERROR, "非待结算，不允许提起线下佣金结算审批！");
            IncomeContract updateIncomeContract = new IncomeContract();
            updateIncomeContract.setId(incomeContract.getId());
            updateIncomeContract.setOfflineCommissionSettleStatus(OfflineCommissionSettleStatusEnum.APPROVAL.getCode());
            incomeContractMapper.updateById(updateIncomeContract);
            approvalFlowService.startProcess(incomeContract.getId(), incomeContract.getContractCode(), ApprovalEnum.INCOME_LIVE_CONTRACT_OFFLINE_COMMISSION, JSON.toJSONString(incomeContract));
        } else if(NumberCst.TWO.equals(status)) {
            //撤回
            BusinessAssert.isTrue(OfflineCommissionSettleStatusEnum.isApproval(incomeContract.getOfflineCommissionSettleStatus()), McnErrorCode.BUSINESS_ERROR, "非结算中，不允许撤回线下佣金结算审批！");
            this.approvalRefuse(incomeContract);
            approvalFlowService.revokeProcess(incomeContract.getId(), ApprovalEnum.INCOME_LIVE_CONTRACT_OFFLINE_COMMISSION.getProcessId());
        }
        //操作日志
        EventPublisher.post(OperateLogEvent.builder().businessId(incomeContract.getId())
                .businessType(LogBusinessTypeEnum.INCOME_CONTRACT)
                .compare(incomeContract, incomeContractMapper.selectById(incomeContract.getId()))
                .build());
        return incomeContractId;
    }

    @Override
    public void invaild(Long incomeContractId) {
        incomeContractMapper.updateByNoStrategy(Wrappers.lambdaUpdate(IncomeContract.class)
                .set(IncomeContract::getOfflineCommissionSettleStatus, OfflineCommissionSettleStatusEnum.UNSETTLED.getCode())
                .set(IncomeContract::getOfflineCommissionAmount, null)
                .set(IncomeContract::getUpdateBy, UserVoContextHolder.getUserId())
                .set(IncomeContract::getUpdateName, UserVoContextHolder.getUserName())
                .set(IncomeContract::getUpdateDate, new Date())
                .eq(IncomeContract::getId, incomeContractId));
        approvalFlowService.revokeProcess(incomeContractId, ApprovalEnum.INCOME_LIVE_CONTRACT_OFFLINE_COMMISSION.getProcessId());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApprovalOperationVO changeApproval(ChangeApprovalDTO dto, Long billId) {
        IncomeContract incomeContract = this.getIncomeContract(billId);
        BusinessAssert.isTrue(OfflineCommissionSettleStatusEnum.isApproval(incomeContract.getOfflineCommissionSettleStatus()), McnErrorCode.BUSINESS_ERROR, "线下佣金非结算中，不允许操作！");
        boolean needLog = true;
        try {
            String approvalParam = JSON.toJSONString(dto.getParamMap());
            if(!dto.getApprovalFlag().booleanValue()) {
                this.approvalRefuse(incomeContract);
            } else {
                if(CommonUtil.containsKeys(dto.getParamMap(), "offlineCommissionAmount")) {
                    this.approvalAgree(dto, incomeContract, approvalParam);
                } else {
                    needLog = false;
                }
            }
            ApprovalOperationVO approvalOperationVO = approvalFlowService.approvalChange(dto.getApprovalId(), approvalParam, dto.getApprovalFlag(), dto.getRemark());
            BusinessAssert.isTrue(approvalOperationVO.isSuccess(), McnErrorCode.FLOW_ERROR, approvalOperationVO.getMessage());
            return approvalOperationVO;
        } finally {
            if(needLog) {
                //操作日志
                EventPublisher.post(OperateLogEvent.builder().businessId(incomeContract.getId())
                        .businessType(LogBusinessTypeEnum.INCOME_CONTRACT)
                        .compare(incomeContract, incomeContractMapper.selectById(billId)).build());
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public NextNotifyVo changeApprovalNext(ChangeApprovalDTO dto, Long billId) {
        IncomeContract incomeContract = this.getIncomeContract(billId);
        BusinessAssert.isTrue(OfflineCommissionSettleStatusEnum.isApproval(incomeContract.getOfflineCommissionSettleStatus()), McnErrorCode.BUSINESS_ERROR, "线下佣金非结算中，不允许操作！");
        boolean needLog = true;
        try {
            String approvalParam = JSON.toJSONString(dto.getParamMap());
            if(!dto.getApprovalFlag().booleanValue()) {
                this.approvalRefuse(incomeContract);;
            } else {
                if(CommonUtil.containsKeys(dto.getParamMap(), "offlineCommissionAmount")) {
                    this.approvalAgree(dto, incomeContract, approvalParam);
                } else {
                    needLog = false;
                }
            }
            return approvalFlowService.approvalChangeNext(dto.getApprovalId(), approvalParam, dto.getApprovalFlag(), dto.getRemark());
        } finally {
            if(needLog) {
                //操作日志
                EventPublisher.post(OperateLogEvent.builder().businessId(incomeContract.getId())
                        .businessType(LogBusinessTypeEnum.INCOME_CONTRACT)
                        .compare(incomeContract, incomeContractMapper.selectById(billId))
                        .build());
            }
        }
    }

    /**
     * 审批-同意
     * @param dto
     * @param incomeContract
     * @param approvalParam
     */
    private void approvalAgree(ChangeApprovalDTO dto, IncomeContract incomeContract, String approvalParam) {
        LiveContractFinanceSettlementDTO financeSettlementDTO = JSON.parseObject(approvalParam, LiveContractFinanceSettlementDTO.class);
        financeSettlementDTO.setApprovalId(dto.getApprovalId());
        financeSettlementDTO.setId(incomeContract.getId());
        ValidatorUtil.validate(financeSettlementDTO);
        IncomeContract updateIncomeContract = new IncomeContract();
        updateIncomeContract.setId(incomeContract.getId());
        updateIncomeContract.setOfflineCommissionAmount(financeSettlementDTO.getOfflineCommissionAmount());
        if(CommonUtil.greaterThanZero(financeSettlementDTO.getOfflineCommissionAmount())) {
            updateIncomeContract.setBusinessAmount(ObjectUtils.defaultIfNull(incomeContract.getBusinessAmount(), BigDecimal.ZERO).add(financeSettlementDTO.getOfflineCommissionAmount()));
        }
        incomeContractMapper.updateById(updateIncomeContract);
    }

    /**
     * 审批-拒绝/撤回
     * @param incomeContract
     */
    private void approvalRefuse(IncomeContract incomeContract) {
        LambdaUpdateWrapper<IncomeContract> lambdaUpdateWrapper = Wrappers.lambdaUpdate(IncomeContract.class);
        //结算审批拒绝、撤回，合同回款状态不变，执行单回款状态不变
        if (CommonUtil.greaterThanZero(incomeContract.getOfflineCommissionAmount())) {
            BigDecimal businessAmount = incomeContract.getBusinessAmount().subtract(incomeContract.getOfflineCommissionAmount());
            lambdaUpdateWrapper.set(IncomeContract::getBusinessAmount, businessAmount);
        }
        incomeContractMapper.updateByNoStrategy(lambdaUpdateWrapper
                .set(IncomeContract::getOfflineCommissionSettleStatus, OfflineCommissionSettleStatusEnum.UNSETTLED.getCode())
                .set(IncomeContract::getOfflineCommissionAmount, null)
                .set(IncomeContract::getUpdateBy, UserVoContextHolder.getUserId())
                .set(IncomeContract::getUpdateName, UserVoContextHolder.getUserName())
                .set(IncomeContract::getUpdateDate, new Date())
                .eq(IncomeContract::getId, incomeContract.getId()));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void approvalCallback(ApprovalFlowDTO approvalFlowDTO, Boolean status) {
        IncomeContract incomeContract = this.getIncomeContract(approvalFlowDTO.getSrcId());
        approvalFlowDTO.setSrcCode(incomeContract.getContractCode());
        approvalFlowDTO.addSubmitInfoIfNull(incomeContract);
        if (Objects.isNull(status)) {
            //审批流配置-接入待办
            return;
        }
        if (!OfflineCommissionSettleStatusEnum.isApproval(incomeContract.getOfflineCommissionSettleStatus())) {
            log.error("直播合同-线下佣金结算流程审批已结束，不做处理！approvalId={}", approvalFlowDTO.getApprovalId());
            return;
        }
        //流程审批通过
        if(status.booleanValue()) {
            Integer offlineCommissionSettleStatus = ObjectUtils.defaultIfNull(incomeContract.getOfflineCommissionAmount(), BigDecimal.ZERO).compareTo(BigDecimal.ZERO) == 1 ?
                    OfflineCommissionSettleStatusEnum.UN_PAYBACK.getCode() : OfflineCommissionSettleStatusEnum.ALL_PAYBACK.getCode();
            LambdaUpdateWrapper<IncomeContract> lambdaUpdateWrapper = Wrappers.lambdaUpdate(IncomeContract.class);
            if(OfflineCommissionSettleStatusEnum.isAllPayback(offlineCommissionSettleStatus)) {
                BigDecimal businessAmount = ObjectUtils.defaultIfNull(incomeContract.getBusinessAmount(), BigDecimal.ZERO);
                if(businessAmount.compareTo(BigDecimal.ZERO) == 0) {
                    //合同总金额为0，结算审批通过，更新合同回款状态为已回款；
                    lambdaUpdateWrapper.set(IncomeContract::getPayBackStatus, NumberCst.TWO);
                } else {
                    // 合同总金额不为0，结算审批通过， 根据回款金额更新合同回款状态；
                    if (BigDecimal.ZERO.compareTo(incomeContract.getPayBackAmount()) == 0) {
                        lambdaUpdateWrapper.set(IncomeContract::getPayBackStatus, NumberCst.ZERO);
                    } else if (businessAmount.compareTo(incomeContract.getPayBackAmount()) == 1) {
                        lambdaUpdateWrapper.set(IncomeContract::getPayBackStatus, NumberCst.ONE);
                    } else if (businessAmount.compareTo(incomeContract.getPayBackAmount()) == 0) {
                        lambdaUpdateWrapper.set(IncomeContract::getPayBackStatus, NumberCst.TWO);
                    } else {
                        lambdaUpdateWrapper.set(IncomeContract::getPayBackStatus, NumberCst.TWO);
                    }
                }
            }
            incomeContractMapper.updateByNoStrategy(lambdaUpdateWrapper
                    .eq(IncomeContract::getId, incomeContract.getId())
                    .set(IncomeContract::getOfflineCommissionSettleStatus, offlineCommissionSettleStatus)
                    .set(IncomeContract::getUpdateBy, UserVoContextHolder.getUserId())
                    .set(IncomeContract::getUpdateName, UserVoContextHolder.getUserName())
                    .set(IncomeContract::getUpdateDate, new Date()));
            //操作日志
            EventPublisher.post(OperateLogEvent.builder().businessId(incomeContract.getId())
                    .businessType(LogBusinessTypeEnum.INCOME_CONTRACT)
                    .compare(incomeContract, incomeContractMapper.selectById(incomeContract.getId()))
                    .build());
            //线下佣金应收金额 = 0，不生成【线下佣金费】
            if(CommonUtil.greaterThanZero(incomeContract.getOfflineCommissionAmount())) {
                TransactionUtils.afterCommit(() -> liveContactOscMessageProducer.asyncSendRecordToMq(incomeContract.getId()));
            }
        }
    }

    /**
     * 获取收入合同
     * @param incomeContractId
     * @return
     */
    private IncomeContract getIncomeContract(Long incomeContractId) {
        //加db悲观锁
        IncomeContract incomeContract = incomeContractMapper.selectOneForUpdate(Wrappers.lambdaQuery(IncomeContract.class)
                .eq(IncomeContract::getId, incomeContractId).eq(IncomeContract::getIsDelete, NumberCst.ZERO));
        BusinessAssert.notNull(incomeContract, McnErrorCode.INCOME_NOT_EXISTS);
        BusinessAssert.notNull(IncomeContractTypeEnum.isLiveContract(incomeContract.getContractType()), "非直播合同，不允许操作");
        BusinessAssert.isTrue(ApprovalStatusEnum.isPassed(incomeContract.getApprovalStatus()), "合同尚未审批通过，不允许操作");
//        String[] typeArray = StringUtils.split(incomeContract.getCooperationType(), SeparatorCst.COMMA);
//        BusinessAssert.isTrue(ArrayUtils.contains(typeArray, "offlineStraightCommission"), "合作类型未选线下佣金，不允许操作");
        return incomeContract;
    }
}
